//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Research.DataLayer
{
    /// <summary>
    /// Collection of security principals (users, groups, etc)
    /// </summary>
    public interface ISecurityPrincipalColection : ICollection<ISecurityPrincipal>
    {
        SystemRoleType currentSystemRole { get; set; }
    }

    /// <summary>
    /// Represents a collection of ISecurityPrincipal objects(users,groups etc).
    /// </summary>
    public class SecurityPrincipalCollection : ISecurityPrincipalColection
    {
        /// <summary>
        /// List holding the ISecurityPrincipal objects.
        /// </summary>
        private List<ISecurityPrincipal> items = new List<ISecurityPrincipal>();

        /// <summary>
        /// The system built-in role type. Will be 'SystemBuiltinRoleType.None' for custom roles.
        /// </summary>
        private SystemBuiltinRoleType systemBuiltinRoleType;

        /// <summary>
        /// The system role type.
        /// </summary>
        private SystemRoleType systemRoleType;

        /// <summary>
        /// The registry connection object.
        /// </summary>
        private Connection registryConn;

        /// <summary>
        /// Gets or sets the current system role.
        /// </summary>
        /// <value>The current system role.</value>
        public SystemRoleType currentSystemRole { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="SecurityPrincipalCollection"/> class.
        /// </summary>
        /// <param name="items">The items.</param>
        /// <param name="builtinRoleType">Type of the builtin role.</param>
        /// <param name="roleType">Type of the role.</param>
        /// <param name="connection">The connection.</param>
        public SecurityPrincipalCollection(List<ISecurityPrincipal> items, SystemBuiltinRoleType builtinRoleType, SystemRoleType roleType, Connection connection)
        {
            this.items.Clear();
            this.items.AddRange(items);
            this.systemBuiltinRoleType = builtinRoleType;
            this.systemRoleType = roleType;
            this.registryConn = connection;
        }

        #region ICollection<ISecurityPrincipal> Members

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>        
        public void Add(ISecurityPrincipal item)
        {
            SystemRole sysRole = SystemRole.Create(this.systemRoleType, this.systemBuiltinRoleType, item, this.registryConn);
            if (!this.registryConn.AutoSave)
            {
                sysRole.Save();
            }
        }

        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>        
        public void Clear()
        {
            while (this.items.Count > 0)
            {
                ISecurityPrincipal item = this.items[0];
                this.Remove(item);
            }
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
        /// </summary>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <returns>
        /// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
        /// </returns>
        public bool Contains(ISecurityPrincipal item)
        {
            return this.items.Contains(item);
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param>
        /// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>       
        public void CopyTo(ISecurityPrincipal[] array, int arrayIndex)
        {
            this.items.CopyTo(array, arrayIndex);
        }

        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        public int Count
        {
            get { return this.items.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
        /// </returns>
        public bool IsReadOnly
        {
            get { return false; }
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>        
        public bool Remove(ISecurityPrincipal item)
        {
            try
            {
                List<SystemRole> sysRoles = new List<SystemRole>();
                SystemRole.ISearch searchQuery = SystemRole.ISearch.Create();
                searchQuery.Query = SystemRole.ISearch.AND(
                    FetchSecurityPrincipalClause(item),
                    FetchSystemRoleClause(this.currentSystemRole));

                sysRoles = SystemRole.Search(searchQuery, this.registryConn);
                SystemRole sysRole = sysRoles[0];               
                
                sysRole.Delete();

                return true;
            }
            catch (Exception)
            {
            }

            return false;
        }

        #endregion

        #region IEnumerable<ISecurityPrincipal> Members

        public IEnumerator<ISecurityPrincipal> GetEnumerator()
        {
            return this.items.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        #endregion

        /// <summary>
        /// Fetches the security principal clause to be used in the search query.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>The search clause. An exception is throw if the item does not belong to one of the
        /// recognised types.
        /// </returns>
        private static SystemRole.ISearch.ISearchClause FetchSecurityPrincipalClause(ISecurityPrincipal item)
        {
            if (item is User)
            {
                return SystemRole.ISearch.MemberUserPrincipal(ObjectFieldBase.Condition.Equals, (User)item);
            }
            else if (item is Group)
            {
                return SystemRole.ISearch.MemberGroupPrincipal(ObjectFieldBase.Condition.Equals, (Group)item);
            }
            else
            {
                throw new NotImplementedException();
            }
        }

        /// <summary>
        /// Fetches the system role clause.
        /// </summary>
        /// <param name="role">The role.</param>
        /// <returns></returns>
        private SystemRole.ISearch.ISearchClause FetchSystemRoleClause(SystemRoleType role)
        {
            return SystemRole.ISearch.Role(EnumFieldBase.Condition.Equals, role);
        }
    }
}
